package gui;

import java.awt.*;
import java.util.*;
import javax.swing.text.*;

/**
 * Die HighlighterAusgabe-Klasse ist fuer das hervorheben von Keywords, Kommentaren
 * und weiteren bestandteilen der Ausgabe-Tabs des Programms zustaendig.
 */

class HighlighterAusgabe extends DefaultStyledDocument
{
	private DefaultStyledDocument doc;
	private Element rootElement;

        private MutableAttributeSet normal;
        private MutableAttributeSet gray;
        private MutableAttributeSet evaluate;
	private MutableAttributeSet keyword;
	private MutableAttributeSet comment;
	private MutableAttributeSet quote;

	private HashSet keywords;
        
        private int mode;

	public HighlighterAusgabe(int mode)
	{
                this.mode = mode;
		doc = this;
		rootElement = doc.getDefaultRootElement();
		putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );

		normal = new SimpleAttributeSet();
		StyleConstants.setForeground(normal, Color.black);
                StyleConstants.setBold(normal, true);
                
                gray = new SimpleAttributeSet();
		StyleConstants.setForeground(gray, Color.gray);

		comment = new SimpleAttributeSet();
		StyleConstants.setForeground(comment, new Color(0, 163, 51));
		StyleConstants.setItalic(comment, true);
                
                evaluate = new SimpleAttributeSet();
		StyleConstants.setForeground(evaluate, Color.blue);
                StyleConstants.setBold(evaluate, true);

		keyword = new SimpleAttributeSet();
		StyleConstants.setForeground(keyword, Color.blue);
                StyleConstants.setBold(keyword, true);

		quote = new SimpleAttributeSet();
		StyleConstants.setForeground(quote, Color.gray);
                StyleConstants.setItalic(quote, true);
                
                keywords = CONST.KEYWORDS()[mode];
                if(keywords == null) 
                    keywords = new HashSet();
	}
        
        public HighlighterAusgabe() {
            this(0);
        }

        @Override
	public void insertString(int offset, String str, AttributeSet a) 
                throws BadLocationException
	{
		super.insertString(offset, str, a);
		processChangedLines(offset, str.length());
	}

        @Override
	public void remove(int offset, int length) throws BadLocationException
	{
		super.remove(offset, length);
		processChangedLines(offset, 0);
	}

	public void processChangedLines(int offset, int length)
		throws BadLocationException
	{
		String content = doc.getText(0, doc.getLength());

		int startLine = rootElement.getElementIndex( offset );
		int endLine = rootElement.getElementIndex( offset + length );

		for (int i = startLine; i <= endLine; i++)
		{
			applyHighlighting(content, i);
		}
	}

	private void applyHighlighting(String content, int line)
		throws BadLocationException
	{
		int startOffset = rootElement.getElement( line ).getStartOffset();
		int endOffset = rootElement.getElement( line ).getEndOffset() - 1;

		int lineLength = endOffset - startOffset;
		int contentLength = content.length();

		if (endOffset >= contentLength)
			endOffset = contentLength - 1;

		doc.setCharacterAttributes(startOffset, lineLength, normal, true);

		int index = content.indexOf(getSingleLineDelimiter(), startOffset);

		if ( (index > -1) && (index < endOffset) )
		{
			doc.setCharacterAttributes(index, endOffset - index + 1, evaluate, 
                                false);
			endOffset = index - 1;
		}

		checkForTokens(content, startOffset, endOffset);
	}

	private void checkForTokens(String content, int startOffset, int endOffset)
	{
		while (startOffset <= endOffset)
		{
			while (isDelimiter( content.substring(startOffset, startOffset + 1) ))
			{
				if (startOffset < endOffset)
					startOffset++;
				else
					return;
			}

			if (isQuoteDelimiter(content.substring(startOffset, startOffset + 1)))
				startOffset = getQuoteToken(content,startOffset,endOffset);
                        else if ( isStartAssertionDelimiter( content.substring(startOffset, 
                                    startOffset + 1) ) )
				startOffset = getAssertionToken(content,startOffset,endOffset);
                        else if ( isStartEnvironmentDelimiter( content.substring(startOffset, 
                                    startOffset + 1) ) )
				startOffset = getEnvironmentToken(content,startOffset,
                                                                                endOffset);
			else
				startOffset = getOtherToken(content, startOffset, endOffset);
		}
	}

	private int getQuoteToken(String content, int startOffset, int endOffset)
	{
		String quoteDelimiter = content.substring(startOffset, startOffset + 1);
		String escapeString = getEscapeString(quoteDelimiter);

		int index;
		int endOfQuote = startOffset;

		index = content.indexOf(escapeString, endOfQuote + 1);

		while ( (index > -1) && (index < endOffset) )
		{
			endOfQuote = index + 1;
			index = content.indexOf(escapeString, endOfQuote);
		}

		index = content.indexOf(quoteDelimiter, endOfQuote + 1);

		if ( (index < 0) || (index > endOffset) )
			endOfQuote = endOffset;
		else
			endOfQuote = index;

		doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, quote, 
                                                                                        false);

		return endOfQuote + 1;
	}

	private int getAssertionToken(String content, int startOffset, int endOffset)
	{
		String assertionDelimiter = content.substring(startOffset, startOffset + 1);
		String escapeString = getEscapeString(assertionDelimiter);

		int index;
		int endOfQuote = startOffset;

		index = content.indexOf("}", endOfQuote + 1);

		if ( (index < 0) || (index > endOffset) )
			endOfQuote = endOffset;
		else
			endOfQuote = index;

		doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, comment, 
                                                                                        false);

		return endOfQuote + 1;
	}

	private int getEnvironmentToken(String content, int startOffset, int endOffset)
	{
		String environmentDelimiter = content.substring(startOffset, startOffset + 1);
		String escapeString = getEscapeString(environmentDelimiter);

		int index;
		int endOfQuote = startOffset;

		index = content.indexOf("]", endOfQuote + 1);
                
                while ( (index > -1) && (index < endOffset) )
		{
			endOfQuote = index + 1;
			index = content.indexOf("]", endOfQuote);
		}

                index = content.indexOf("]", endOfQuote - 1);
                
		if ( (index < 0) || (index > endOffset) )
			endOfQuote = endOffset;
		else
			endOfQuote = index;

		doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, quote, 
                                                                                        false);

		return endOfQuote + 1;
	}

	private int getOtherToken(String content, int startOffset, int endOffset)
	{
		int endOfToken = startOffset + 1;

		while ( endOfToken <= endOffset )
		{
			if ( isDelimiter( content.substring(endOfToken, endOfToken + 1) ) )
				break;

			endOfToken++;
		}

		String token = content.substring(startOffset, endOfToken);

		if ( isKeyword( token ) )
		{
			doc.setCharacterAttributes(startOffset, endOfToken - startOffset, 
                                                                               keyword, false);
		}

		return endOfToken + 1;
	}

	protected boolean isDelimiter(String character)
	{
                String operands;
                if(mode == CONST.MODE_ADA())
                    operands = ";:{}()+/%<=>!&|^~*";
                else if(mode == CONST.MODE_WHILE())
                    operands = ";:()+-/%<=>!&|^~*";
                else
                    operands = "";
		
		if (Character.isWhitespace( character.charAt(0) ) ||
			operands.indexOf(character) != -1 )
			return true;
		else
			return false;
	}

	protected boolean isStartEnvironmentDelimiter(String character)
	{
		String quoteDelimiters = "[";

		if (quoteDelimiters.indexOf(character) < 0)
			return false;
		else
			return true;
	}

	protected boolean isStartAssertionDelimiter(String character)
	{
                String quoteDelimiters;
                if(mode == CONST.MODE_ADA())
                    quoteDelimiters = "--";
                else if(mode == CONST.MODE_WHILE())
                    quoteDelimiters = "{";
                else 
                    quoteDelimiters = "";

		if (quoteDelimiters.indexOf(character) < 0)
			return false;
		else
			return true;
	}
        
	protected boolean isQuoteDelimiter(String character)
	{
		String quoteDelimiters = "\"'";

		if (quoteDelimiters.indexOf(character) < 0)
			return false;
		else
			return true;
	}

	protected boolean isKeyword(String token)
	{
		return keywords.contains( token.toLowerCase() );
	}

	protected String getSingleLineDelimiter()
	{
            if(mode == CONST.MODE_WHILE())
		return "=>";
            else if(mode == CONST.MODE_ADA())
                return "=>";
            else return "";
	}

	protected String getEscapeString(String quoteDelimiter)
	{
		return "\\" + quoteDelimiter;
	}
        
        public static EditorKit getEditorKit()
        {
           return new StyledEditorKit()
           {
              @Override
              public Document createDefaultDocument()
              {
                      return new HighlighterAusgabe();
              }
           };
        }
}